﻿#region Copyright and License Notice
// Copyright (C)2010-2016 - Rob Levine and other contributors
// http://configgen.codeplex.com
// 
// This file is part of ConfigGen.
// 
// ConfigGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// ConfigGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License and 
// the GNU Lesser General Public License along with ConfigGen.  
// If not, see <http://www.gnu.org/licenses/>
#endregion

using System;
using System.Collections.Generic;

namespace ConfigGen.ConsoleApp.Core.CommandLineOptionCore
{
    /// <summary>
    /// Parses and executes command line options.
    /// </summary>
    /// <typeparam name="T">Type of settings object that the command line option processors act upon.</typeparam>
    public class CommandLineOptionExecutor<T>
    {
        private readonly ICommandLineOptionFactory<T> _factory;

        /// <summary>
        /// Initializes a new instance of the <see cref="CommandLineOptionExecutor&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="factory">Factory for providing individual command line option processors</param>
        public CommandLineOptionExecutor(ICommandLineOptionFactory<T> factory)
        {
            if (factory == null) throw new ArgumentNullException("factory");
            _factory = factory;
        }

        /// <summary>
        /// Parses the supplied command line arguments into a collection of <see cref="ICommandLineOption{T}"/> instances
        /// and executes them. The supplied settings object is passed into each processor.
        /// </summary>
        /// <remarks>All <see cref="ICommandLineOption{T}"/> are created and have their
        /// <see cref="ICommandLineOption{T}.ConsumeArgumentsFromQueue"/> method called first. Once this is complete for
        /// all processors, then the <see cref="ICommandLineOption{T}.ApplyOptionToSettings"/> methods are called on each.</remarks>
        /// <param name="args">Collection of arguments supplied at the command line.</param>
        /// <param name="settings">Settings object to be passed to each processor.</param>
        /// <exception cref="ArgumentException">Thrown if an unexpected item is encountered in the args list.</exception>
        public void ProcessAndExecuteCommandLineOptions(IEnumerable<string> args, T settings)
        {
            var argQueue = new Queue<string>(args);
            var argHandlers = new List<ICommandLineOption<T>>();

            while (argQueue.Count > 0)
            {
                string currentArg = argQueue.Dequeue();
                ICommandLineOption<T> argHandler;

                if (currentArg.StartsWith("--"))
                {
                    argHandler = _factory.GetByLongName(currentArg.Substring(2));                 
                }
                else if (currentArg.StartsWith("-"))
                {
                    argHandler = _factory.GetByShortName(currentArg.Substring(1));
                }
                else
                {
                    throw new ArgumentException(
                        @"Unexpected item on command line. Expected to start with either ""-"" or ""--"", but was: " + currentArg);
                }

                argHandler.ConsumeArgumentsFromQueue(argQueue);
                argHandlers.Add(argHandler);
            }

            foreach (var argHandler in argHandlers)
            {
                argHandler.ApplyOptionToSettings(settings);
            }
        }
    }
}
